package com.fengxu.gamescript.jsengine.api.music;

import android.annotation.SuppressLint;
import android.graphics.Color;
import android.view.View;
import android.view.WindowManager;
import android.widget.SeekBar;
import android.widget.TextView;

import com.fengxu.gamescript.R;
import com.fengxu.gamescript.custom.RingView;
import com.fengxu.gamescript.data.KeyConf;
import com.fengxu.gamescript.data.other.KeyConfData;
import com.fengxu.gamescript.data.other.Point;
import com.fengxu.gamescript.jsengine.annotation.JsCallJava;
import com.fengxu.gamescript.jsengine.annotation.JsCodeTip;
import com.fengxu.gamescript.jsengine.annotation.JsObject;
import com.fengxu.gamescript.jsengine.api.Global;
import com.fengxu.gamescript.jsengine.exception.ScriptRunningException;
import com.fengxu.gamescript.service.AutoAccessibilityService;
import com.fengxu.gamescript.util.CommonUtils;
import com.fengxu.gamescript.util.FloatUtils;
import com.xuexiang.xui.utils.DensityUtils;
import com.yhao.floatwindow.FloatWindow;
import com.yhao.floatwindow.IFloatWindow;
import com.yhao.floatwindow.MoveType;

import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;

import kotlin.Unit;

/**
 * 原神自动点击类
 */
@JsObject(name = "ys")
public class GenshinAutoClick extends AbstractMusicAutoClick {

    // 当前使用过的键位参数信息
    private KeyConfData activeKeyConfData;

    // 从激活键位中调整后的对应21键位的兼容键位
    private Point[] compatiblePoints = new Point[21];

    // 对应电脑键位的映射
    public static final char[][] keyMap = {
            {'Q','W','E','R','T','Y','U'},
            {'A','S','D','F','G','H','J'},
            {'Z','X','C','V','B','N','M'}
    };

    // 将原神21键的顺序键位索引对应音量从低到高的索引(1-21分布)
    public static final int[] volumeLowToHighArr = {
      15,16,17,18,19,20,21,8,9,10,11,12,13,14,1,2,3,4,5,6,7
    };

    // 是否显示动画效果
    @JsCodeTip
    public boolean showAnim = false;
    // 是否处于调试模式
    @JsCodeTip
    public boolean debug = false;


    @Override
    public String getNameInJs() {
        return "ys";
    }

    public GenshinAutoClick(Global global){
        super(global);
        this.activeKeyConfData = KeyConf.readKeyConf(global.getmContext()).getActiveKeyConfData();
        // 对用户激活键位配置中的键位按音调从低到高映射至21键位
        if(activeKeyConfData.isSeries()){
            // 音调连续则直接映射
            for (int i = 0; i < activeKeyConfData.getPoints().length; i++) {
                compatiblePoints[i] = activeKeyConfData.getPoints()[i];
            }
        }else{
            // 编辑每行已经映射了的个数也就是列,最大3x7，所以这里设置三行
            int[] mappedRow = {0,0,0};
            // 用户配置的键位索引当前扫描到的位置
            int scanIndex = 0;
            // 不连续则分段映射
            for (int i = 0; i < compatiblePoints.length && scanIndex  < activeKeyConfData.getPoints().length; i++) {
                int rowIndex = i / 7;
                if(rowIndex < activeKeyConfData.getRow() && mappedRow[rowIndex] < activeKeyConfData.getColumn()){
                    compatiblePoints[i] = activeKeyConfData.getPoints()[scanIndex++];
                    mappedRow[rowIndex] += 1;
                }
            }
        }
    }

    /**
     * 在指定的键位处显示圆环动画特效
     */
    private void showAnimInKeyPos(int x,int y){
        RingView view = new RingView(global.getmContext());
        view.setClickable(false);
        view.setRadius(DensityUtils.dp2px(10));
        view.setMaxRadius(DensityUtils.dp2px(40));
        view.setRingColor(Color.parseColor("#03A9F4"));
        view.setRingEndColor(Color.parseColor("#F0FFFF"));
        view.setRingWidth(DensityUtils.dp2px(5F));
        int width = DensityUtils.dp2px(90);
        WindowManager windowManager = FloatUtils.INSTANCE.getWindowManager(global.getmContext());
        WindowManager.LayoutParams lp = FloatUtils.INSTANCE.getDefaultFloatParams();
        lp.flags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
        lp.width = width; lp.height = width;
        lp.x = x - width / 2 ; lp.y = y - width / 2;
        global.getUiHandle().post(()->{
            windowManager.addView(view, lp);
            view.showAnim(1000,()->{
                windowManager.removeView(view);
                return Unit.INSTANCE;
            });
        });
    }

    /**
     * 将用于传入的基于21键的索引，解析为兼容用户当前应用的键位方案对应的键位索引
     * @return 处理后的用户键位索引，小于0表示未匹配
     */
    private int parseIndexToCompatibleIndex(int sourceIndex){
        int targetIndex = 0;
        // 获取对应的用户配置的键位索引
        for (;targetIndex < volumeLowToHighArr.length; targetIndex++){
            if(volumeLowToHighArr[targetIndex] == sourceIndex){
                break;
            }
        }
        // 判断已经映射的兼容键位中有没有targetIndex对应的坐标点
        // 没有则判断用户对该点是否进行将调处理
        if(activeKeyConfData.isAbateHighVolume()){
            // 没有对应则进行降调处理
            while (compatiblePoints[targetIndex] == null){
                // 7为一个音调
                targetIndex -= 7;
                // 小于0表示降调任未匹配，此时不点击
                if(targetIndex < 0){
                    break;
                }
            }
        }else{
            // 没有对应则不点击
            if(compatiblePoints[targetIndex] == null){
                targetIndex = -1;
            }
        }
        return targetIndex;
    }

    /**
     * 点击总索引1-21
     * @param allIndex 总索引 第一行 1-7 高音 第二行 8-14 中音 第三行 15-21 低音
     */
    @JsCodeTip
    public void press(int allIndex){
        if(activeKeyConfData == null){
            global.getUiHandle().post(()->{
                CommonUtils.INSTANCE.showDesktopToast(global.getmContext(),"您未应用任何的键位配置方案,请先配置键位!",5000);
            });
            throw new ScriptRunningException("您未应用任何的键位配置方案,请先配置键位!");
        }
        if(allIndex < 1 || allIndex > 21){
            throw new ScriptRunningException("索引应在1~21范围内!");
        }
        int targetIndex = parseIndexToCompatibleIndex(allIndex);
        if(targetIndex < 0) return;
        Point point = compatiblePoints[targetIndex];
        if(!debug){
            AutoAccessibilityService service = Global.getAccessibilityService();
            service.autoClick(point.getX(), point.getY(), 1);
        }else{
            // 播放对应模拟音
            playMusic(volumeLowToHighArr[targetIndex] - 1);
        }
        if(showAnim){
            showAnimInKeyPos(point.getX().intValue(), point.getY().intValue());
        }
    }

    /**
     * 单击原神琴键索引
     * @param x 横向索引,从1开始
     * @param y 纵向索引，从1开始
     */
    @JsCodeTip
    public void press(int x, int y){
       int allIndex = (y-1)*7 + x;
       press(allIndex);
    }


    /**
     * 通过电脑键位点击相应位置
     * @param key Q-U A-J Z-M keyMap中
     */
    @JsCodeTip
    public void press(String key){
        if(key == null || key.length() != 1){
            throw new ScriptRunningException("原神键位点击错误,key只能为长度为一的字符");
        }
        char  k = key.toUpperCase().charAt(0);
        boolean isFound = false;
        for (int y = 0; y < keyMap.length; y++) {
            for(int x=0; x < keyMap[y].length; x++){
                if(keyMap[y][x] == k){
                    press(x+1,y+1);
                    isFound = true;
                    break;
                }
            }
        }
        if(!isFound){
            throw new RuntimeException(String.format("%s不在对应的键位中!",key));
        }
    }


    // 要点击的键位
    private List<MusicKeyInfo> musicKeyInfoList = new LinkedList<>();

    // 是否开始演奏
    private volatile boolean isStart = false;

    // 演奏索引
    private volatile int musicIndex = 0;

    // 是否退出
    private volatile boolean isExit = false;

    // 音乐名称
    private String musicName = "";

    // 音乐播放速录
    private volatile float speed = 1.0F;

    // 是否处于用户拖拽进度条状态中
    private volatile boolean isInDrag = false;

    // 音乐悬浮窗名称
    private String floatWinName;

    /**
     * 解析原神音乐字符串
     * @param musicName 音乐名
     * @param str  要点击的键位和暂停时间特殊字符格式
     */
    @JsCallJava
    public void parseGenshinImpactMusic(String musicName, String str) {
        this.musicName = musicName;
        floatWinName = "music" + Thread.currentThread().getName() + musicName;
        parseMusicStr(str);
        global.getUiHandle().post(()->{
            showFlow();
        });
        while (!isExit){
            try {
                startPlayMusic();
            } catch (Exception e) {
                FloatWindow.destroy(floatWinName);
                releaseMusic();
                throw new ScriptRunningException(e.getMessage());
            }
        }
        global.exit();
    }

    /**
     * 开始演奏
     */
    private void startPlayMusic() throws InterruptedException {
        for (musicIndex = 0; musicIndex < musicKeyInfoList.size() && !isExit; musicIndex++) {
            if(isStart){
                MusicKeyInfo keyInfo = musicKeyInfoList.get(musicIndex);
                if(keyInfo.key != 0){
                    press(String.valueOf(keyInfo.key));
                }else{
                    Thread.sleep((long) (keyInfo.sleep / speed));
                }
            }else{
                Thread.sleep(50);
                musicIndex--;
            }
            global.getUiHandle().post(()-> {
                IFloatWindow window = FloatWindow.get(floatWinName);
                if(window == null || isInDrag) return;
                SeekBar seekBar = window.getView().findViewById(R.id.flow_music_progress);
                seekBar.setProgress(musicIndex * 100 / musicKeyInfoList.size());
            });
        }
        isStart = false;
        global.getUiHandle().post(()-> {
            IFloatWindow window = FloatWindow.get(floatWinName);
            if(window == null) return;
            ((TextView)window.getView().findViewById(R.id.flow_music_start)).setText("开始演奏");
        });
    }

    /**
     * 解析特殊的音乐字符
     * @param str
     */
    private void parseMusicStr(String str) {
        musicKeyInfoList.clear();
        int sleepTime = 0;
        for (char c : str.toCharArray()) {
            if(Character.isLetter(c)){
                // 是字符
                if(sleepTime > 0){
                    musicKeyInfoList.add(new MusicKeyInfo((char) 0,sleepTime));
                    sleepTime = 0;
                }
                musicKeyInfoList.add(new MusicKeyInfo(c, 0));
            }else{
                // 其他符号
                switch (c){
                    case ',':
                        sleepTime += 240; break;
                    case '-':
                        sleepTime += 120; break;
                    case '~':
                        sleepTime += 60; break;
                    case '#':
                        sleepTime += 10; break;
                    default:
                        throw new ScriptRunningException("未能解析的字符:\"" + c +"\"!");
                }
            }
        }
    }

    /**
     * 初始化音乐脚本操作悬浮窗
     */
    private void showFlow(){
        View view = initFlowView();
        int viewWidth = DensityUtils.dp2px(280);
        FloatWindow.with(global.getmContext().getApplicationContext())
                .setView(view)
                .setWidth(viewWidth)
                .setMoveType(MoveType.slide)
                .setDesktopShow(true)
                .setTag(floatWinName)
                .build();
        FloatWindow.get(floatWinName).show();
    }

    /**
     * 初始化悬浮窗中的view
     */
    @SuppressLint("DefaultLocale")
    private View initFlowView(){
        View view = View.inflate(global.getmContext(), R.layout.flow_music_script, null);
        ((TextView)view.findViewById(R.id.flow_music_name)).setText(musicName);
        TextView speedView = (TextView) view.findViewById(R.id.flow_music_speed);
        // 对控制组件从上到下分层(不包括标题栏)
        View viewPart1 = view.findViewById(R.id.flow_music_content_part1); // 折叠模式层
        View viewPart2 = view.findViewById(R.id.flow_music_content_part2); // 速率控制层
        View viewPart3 = view.findViewById(R.id.flow_music_content_part3); // 窗口退出层
        View viewPart4 = view.findViewById(R.id.flow_music_content_part4); // 进度控制层
        // 点击主体显示所有被隐藏的层
        view.setOnClickListener(v->{
            viewPart1.setVisibility(View.VISIBLE);
            viewPart2.setVisibility(View.VISIBLE);
            viewPart3.setVisibility(View.VISIBLE);
            viewPart4.setVisibility(View.VISIBLE);
        });
        // 折叠模式1，只显示进度控制层
        view.findViewById(R.id.flow_music_mode1).setOnClickListener(v->{
            viewPart1.setVisibility(View.GONE);
            viewPart2.setVisibility(View.GONE);
            viewPart3.setVisibility(View.GONE);
        });
        // 折叠模式2，只显示速率控制层
        view.findViewById(R.id.flow_music_mode2).setOnClickListener(v->{
            viewPart1.setVisibility(View.GONE);
            viewPart3.setVisibility(View.GONE);
            viewPart4.setVisibility(View.GONE);
        });
        // 加速
        view.findViewById(R.id.flow_music_addSpeed).setOnClickListener(v->{
            speed += 0.1;
            speedView.setText(String.format("%.1f",speed));
        });
        // 降速
        view.findViewById(R.id.flow_music_lowSpeed).setOnClickListener(v->{
            if(speed - 0.1 > 0){
                speed -= 0.1;
                speedView.setText(String.format("%.1f",speed));
            }
        });
        // 开始/暂停
        view.findViewById(R.id.flow_music_start).setOnClickListener(v -> {
            isStart = !isStart;
            if(isStart){
                ((TextView)v).setText("暂停演奏");
            }else{
                ((TextView)v).setText("开始演奏");
            }
        });
        // 退出
        view.findViewById(R.id.flow_music_exit).setOnClickListener(v-> {
            isExit = true;
            FloatWindow.destroy(floatWinName);
        });
        ((SeekBar)view.findViewById(R.id.flow_music_progress)).setOnSeekBarChangeListener(new MySeekbarProgressChanged());
        return view;
    }

    /**
     * 监听拖动条的进度变化事件
     */
    private class MySeekbarProgressChanged implements SeekBar.OnSeekBarChangeListener{

        @Override
        public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
            if(fromUser){
                musicIndex = (int) (progress / 100.0 * musicKeyInfoList.size());
            }
        }

        @Override
        public void onStartTrackingTouch(SeekBar seekBar) {
            isInDrag = true;
        }

        @Override
        public void onStopTrackingTouch(SeekBar seekBar) {
            isInDrag = false;
        }
    }

    /**
     * 要点击的键位映射实体类
     */
    private static class MusicKeyInfo{
        public char key = 0;    // 要点击的键位 当key=0时表示什么都不点
        public int  sleep = 2;  // 暂停的事件

        public MusicKeyInfo(char key, int sleep) {
            this.key = key;
            this.sleep = sleep;
        }
    }

}
